﻿using Newtonsoft.Json;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Security.Cryptography;
using System.Xml;

namespace Wargame_V3_1
{
    public class ShootJudge
    {
        /// <summary>
        /// 从文件中读取数据，赋值给私有字段
        /// </summary>
        /// <param name="Path"></param>
        public ShootJudge(string path)
        {
            // 根据xml文件初始化weapons，每个xml文件对应一个weapon
            string weaponPath = path + @"\weapons\";
            string[] weaponFiles = Directory.GetFiles(weaponPath, "*.xml", SearchOption.TopDirectoryOnly);  // 只在本级路径中搜索文件

            foreach (string weaponFile in weaponFiles)
            {
                Weapon weapon = GetWeaponFromFile(weaponFile);
                weapons.Add(weapon.Name, weapon);
            }

            // 根据Json文件初始化elevRectTab表格
            string elevRectTabString = File.ReadAllText(Path.Combine(path, "elev_rect_tab.json"));
            elevRectTab = JsonConvert.DeserializeObject<int[,]>(elevRectTabString);

            // 根据Json文件初始化attackLevel2OriDamageTab表格
            string attackLevel2OriDamageTabString = File.ReadAllText(Path.Combine(path, "attack_level_2_ori_damage_tab.json"));
            attackLevel2OriDamageTab = JsonConvert.DeserializeObject<int[,]>(attackLevel2OriDamageTabString);

            // 根据Json文件初始化originDamageTab表格
            string originDamageTabString = File.ReadAllText(Path.Combine(path, "origin_damage_tab.json"));
            originDamageTab = JsonConvert.DeserializeObject<int[,]>(originDamageTabString);

            // 根据Json文件初始化damageRectTab表格
            string damageRectTabString = File.ReadAllText(Path.Combine(path, "damage_rect_tab.json"));
            damageRectTab = JsonConvert.DeserializeObject<int[,]>(damageRectTabString);

            // 根据Json文件初始化toInfantryOriDamageTab表格
            string toInfantryOriDamageTabString = File.ReadAllText(Path.Combine(path, "to_infantry_ori_damage_tab.json"));
            toInfantryOriDamageTab = JsonConvert.DeserializeObject<int[,]>(toInfantryOriDamageTabString);

            // 根据Json文件初始化toInfantryDamageRectTab表格
            string toInfantryDamageRectTabString = File.ReadAllText(Path.Combine(path, "to_infantry_damage_rect_tab.json"));
            toInfantryDamageRectTab = JsonConvert.DeserializeObject<int[]>(toInfantryDamageRectTabString);
        }

        private Dictionary<string, Weapon> weapons = new Dictionary<string, Weapon>();
        private int[,] elevRectTab;
        /*    1   2   3   4   5   6   7   8   9  10  11  12 --> Distance  
         *  {-2, -2, -1, -1, -1,  0,  0,  0,  0,  0,  0,  0, },  1\
            {-2, -2, -2, -1, -1, -1, -1,  0,  0,  0,  0,  0, },  2 \
            {-3, -2, -2, -2, -1, -1, -1, -1, -1,  0,  0,  0, },  3  \
            {-3, -3, -3, -2, -2, -1, -1, -1, -1, -1, -1,  0, },  4   \ 高度差
            {-4, -3, -3, -3, -2, -2, -2, -1, -1, -1, -1, -1, },  5   / elev_diff
            {-4, -4, -4, -3, -3, -2, -2, -2, -1, -1, -1, -1, },  6  /
            {-5, -4, -4, -4, -3, -2, -2, -2, -2, -1, -1, -1, },  7 /
            {-5, -5, -5, -4, -3, -3, -2, -2, -2, -2, -2, -1, }   8/
         */
        private int[,] attackLevel2OriDamageTab;
        /*   1, 2, 3, 4,  5,  6,  7,  8, 9,  10  --> attack level
            {0, 2, 3, 4,  5,  6,  7,  8, 10, 11 },  // 1 \
            {1, 3, 5, 6,  7,  8,  9, 11, 12, 13 },  // 2  \
            {2, 4, 6, 7,  9, 11, 12, 13, 14, 15 },  // 3   } blood
            {2, 4, 6, 8, 10, 12, 13, 14, 16, 17 },  // 4  /
            {2, 4, 8,10, 13, 14, 15, 17, 18, 19 }   // 5 /
            此表中的值，对应了下面表originDamageTab中的行数，0->第0行，……，19->第19行
         */
        private int[,] originDamageTab;
        /*   2, 3, 4, 5, 6, 7, 8, 9,10,11,12  -> 两个骰子的点数之和
            {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
            {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
            {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
            {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0 },
            {0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
            {1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
            {1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
            {1, 0, 1, 1, 1, 2, 1, 0, 0, 0, 0 },
            {1, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0 },
            {1, 1, 1, 1, 1, 2, 0, 2, 0, 2, 0 },
            {1, 1, 1, 1, 1, 2, 0, 2, 1, 2, 0 },
            {1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0 },
            {1, 1, 1, 1, 1, 3, 2, 2, 2, 0, 1 },
            {2, 2, 1, 1, 1, 3, 2, 2, 2, 2, 2 },
            {3, 3, 1, 1, 1, 3, 2, 2, 2, 3, 3 },
            {3, 3, 1, 1, 1, 4, 2, 2, 2, 3, 3 },
            {2, 2, 1, 1, 1, 3, 4, 4, 4, 2, 3 },
            {3, 1, 1, 1, 3, 4, 4, 3, 4, 2, 2 },
            {5, 1, 1, 1, 5, 3, 4, 4, 4, 3, 4 }
         */
        private int[,] damageRectTab;
        /*    <=-3 -2  -1   0   1   2   3   4   5   6   7   8   9  10  11  >=12  ->修正后的随机数
            {   0,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1,  1,  1,  2,  2,  3 }, // 无装甲
            {  -1,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,  2,  2,  3 }, // 轻型装甲
            {  -2, -1, -1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  2,  3 }, // 中型装甲
            {  -3, -2, -2, -1, -1,  0,  0,  0,  0,  0,  0,  0,  0,  1,  2,  2 }, // 重型装甲
            {  -3, -3, -3, -3, -2, -2, -1, -1, -1,  0,  0,  0,  0,  0,  1,  2 }, // 复合装甲
         */
        private int[,] toInfantryOriDamageTab;
        private int[] toInfantryDamageRectTab;

        public Dictionary<string, object> ToJudge(dynamic interact, Operator thisOpr, Map map, double curStep)
        {
            dynamic type = thisOpr.GetAttri("Type");
            dynamic subType = thisOpr.GetAttri("SubType");
            dynamic moveState = thisOpr.GetAttri("MoveState");
            dynamic curHex = thisOpr.GetAttri("CurHex");
            dynamic objID = thisOpr.GetAttri("ObjID");
            dynamic color = thisOpr.GetAttri("Color");
            dynamic speed = thisOpr.GetAttri("Speed");
            dynamic stack = thisOpr.GetAttri("Stack");
            dynamic armor = thisOpr.GetAttri("Armor");

            int distance = map.GetDistance(interact.CurHex, curHex);// 与敌方的距离
            // 根据双方算子的类型，位置，观察距离，调用Map类的CanSeeTerrain，判断是否可以通视
            bool can_see = map.CanSeeTerrain(interact.AttType, type, interact.AttCurHex, curHex, subType, interact.AttObserveDistance, moveState);
            // 根据是否可视，剩余子弹数，武器id，敌方算子类型，距离，我方算子血量，调用ShootJudgeTable类的CalcAttackLevel方法查表得到攻击等级
            int attack_level = CalcAttackLevel(can_see, interact.Weapon, type, distance, interact.AttBlood);
            if (attack_level < 0)// 攻击等级小于0
            {
                Trace.TraceWarning($"无效交互：攻击等级小于0，无效射击");
                return new Dictionary<string, object>
                {
                    {"IsSuccess", false },
                    {"AttLevel", attack_level },
                    {"AttObjID", interact.AttObjID },
                    {"AttBlood", interact.AttBlood },
                    {"AttColor", interact.AttColor },
                    {"AttSubType", interact.AttSubType },
                    {"CurStep", curStep},
                    {"Damage", null },
                    {"Distance", distance },
                    {"EleDiff", null },
                    {"OriDamage", null },
                    {"Random1", null },
                    {"Random2", null },
                    {"Random2Rect", null },
                    {"RectDamage", null },
                    {"TargetObjID", objID },
                    {"TargetColor", color },
                    {"TargetSubType", subType },
                    {"Type", "Shoot" },
                    {"weapon", interact.Weapon },
                    {"Keep", null }
                };
            }

            //判断能否射击
            bool flag_shoot = true;// 标志位
            int elev_rect;// 计算高度差修正
            bool flag_keep = false;// 裁决结果是否为纯压制的标志位
            // elev_diff为射击方与被射击方之间的高度差
            int elev_diff = -(map[interact.AttCurHex[0], interact.AttCurHex[1]].elev - map[curHex[0], curHex[1]].elev) / map.ElevGrade;
            if (elev_diff > 8) elev_diff = 8;// 如果高差大于8则按8计算
            if (elev_diff <= 0 | distance > 12) elev_rect = 0;//如果是 高打低 或者 距离远  则不做高差修正
            else
            {
                elev_rect = GetElevRect(elev_diff, distance);// 否则就调用shootJudgeTable中的高差修正表，对攻击等级修正（elev_rect为小于等于0的整数）
            }
            if (attack_level >= -elev_rect) attack_level += elev_rect;// 如果攻击等级被修正后大于0则正常修正
            else attack_level = 0;// 如果小于0就修正为0

            // 第一个随机数random1用于获取初始战斗结果ori_damage,  random2在进行战损结果修正之后得到random2_rect， random2_rect取得战损结果修正值rect_damage， 对ori_damage修正后得到最终战损damage
            int random1, random2, random2_rect, rect_damage, damage, ori_damage;
            // 攻击等级小于等于0，不造成伤害  变量附上初始值用于直接形成裁决结果
            if (attack_level <= 0)
            {
                random1 = -1;
                random2 = -1;
                random2_rect = -1;
                ori_damage = 0;
                rect_damage = 0;
                damage = -1;
            }
            else
            {
                // 计算origin_damage
                Random random = new Random();
                int r11 = random.Next(1, 7);
                int r12 = random.Next(1, 7);
                random1 = r11 + r12;            // 2到12 随机数
                if (random1 < 2) random1 = 2;
                if (random1 > 12) random1 = 12;
                if (interact.Weapon == "InfantryLightWeapon")// 步兵轻武器
                {
                    //(直瞄武器对人员/)步兵轻武器对车辆战斗结果表
                    ori_damage = GetToInfantryOriDamageTab(random1, attack_level);// 取得初始战损
                }
                else
                {
                    //直瞄对车辆战斗结果表
                    int ori_damage_row = GetOriDamageTabRow(interact.AttBlood, attack_level);
                    ori_damage = GetOriDamage(ori_damage_row, random1);// 取得初始战损
                }
                if (ori_damage == -1)// damage为-1表示裁决结果damage为0且压制
                {
                    flag_keep = true;// 压制标志位为true
                    ori_damage = 0;// 战损置为零（没有伤害的纯压制）
                }

                // 计算随机数2修正值random2_rect
                int val = 0;// 记录random2将会被修正多少
                if (interact.AttKeep) val -= 1;// 射击算子被压制
                if (interact.AttSpeed > 0) val -= 1;//正在机动
                if (interact.AttSubType == "Tank" &&  // 如果坦克算子在水上地形
                    map[interact.AttCurHex / 100, interact.AttCurHex % 100].cond == TerrainType.River)
                {
                    val -= 1;
                }
                // 射击目标在城镇、丛林、河流
                if (map[curHex[0], curHex[1]].cond == TerrainType.City) val -= 1;
                if (map[curHex[0], curHex[1]].cond == TerrainType.Forest) val -= 2;
                if (map[curHex[0], curHex[1]].cond == TerrainType.River) val -= 2;
                // 射击目标正在掩蔽、机动、堆叠、行军
                if (moveState == "Shelt") val -= 2;
                if (speed > 0) val -= 2;
                if (stack) val += 2;
                if (moveState == "March") val += 4;

                // 生成2-12的随机数2
                int r21 = random.Next(1, 7);
                int r22 = random.Next(1, 7);
                random2 = r21 + r22;
                random2_rect = random2 + val;// 用val对随机数2进行修正

                // 计算最终战损damage
                int random2_rect_ind;// 将random2_rect限定在-3到12之间，赋值给random2_rect_ind用于查表
                if (random2_rect <= -3) random2_rect_ind = -3;
                else if (random2_rect >= 12) random2_rect_ind = 12;
                else random2_rect_ind = random2_rect;

                // 查表取得战损修正值
                rect_damage = GetDamageRect(armor, random2_rect_ind);

                // 对初始战损修正，形成最终战损
                damage = ori_damage + rect_damage;
                // 如果最终战损damage为0且无压制，为无效射击
                if (damage == 0)
                {
                    if (!flag_keep)
                    {
                        damage = -1;
                    }
                }
            }
            // 根据上面得到的结果修改judge_info信息
            if (damage < 0) flag_shoot = false;// 最终战损小于0为无效射击

            return new Dictionary<string, object>()
            {
                {"IsSuccess", flag_shoot },
                {"AttLevel", attack_level },
                {"AttObjID", interact.AttObjID },
                {"AttBlood", interact.AttBlood },
                {"AttColor", interact.AttColor },
                {"AttSubType", interact.AttSubType },
                {"CurStep", curStep},
                {"Damage", damage },
                {"Distance", distance },
                {"EleDiff", elev_diff },
                {"OriDamage", ori_damage },
                {"Random1", random1 },
                {"Random2", random2 },
                {"Random2Rect", random2_rect },
                {"RectDamage", rect_damage },
                {"TargetObjID", objID },
                {"TargetColor", color },
                {"TargetSubType", subType },
                {"Type", "Shoot" },
                {"weapon", interact.Weapon },
                {"Keep", flag_keep || damage > 0 }
            };
        }

        public Weapon GetWeapon(string weaponName)
        {
            if (weapons.Keys.Contains(weaponName))
            {
                return weapons[weaponName];
            }
            else
            {
                Trace.TraceError($"输入的武器名称{weaponName}不在裁决表中");
                return null;
            }
        }

        public int CalcAttackLevel(bool can_see, string weaponName, string targetType, int distance, int opr_blood = 4)
        {
            if (!can_see) return -1;  // 不可视，即不可射
            Weapon weapon = GetWeapon(weaponName);
            if (weapon == null) return -1;  // 无此武器的裁决方法
            return weapon.GetAttackLevel(targetType, distance, opr_blood);
        }

        public int GetElevRect(int elevDiff, int distance)
        {
            if (elevDiff <= 0) return 0;
            if (distance <= 0) return 0;
            if (distance > 12) return 0;
            if (elevDiff > 8) elevDiff = 8;
            return elevRectTab[elevDiff - 1, distance - 1];  // 数组从0开始计数
        }

        public int GetOriDamageTabRow(int blood, int attackLevel)
        {
            if (blood <= 0) blood = 1;
            if (blood > 5) blood = 5;
            if (attackLevel <= 0)   // 出错
            {
                Trace.TraceError("攻击等级Attack Level计算有误，必须大于0！");
                return -1;
            }
            if (attackLevel > 10) attackLevel = 10;
            return attackLevel2OriDamageTab[blood - 1, attackLevel - 1];  // 数组从0开始计数
        }

        public int GetOriDamage(int oriDamageTabRow, int randomValue)
        {
            if (oriDamageTabRow < 0 || oriDamageTabRow > 19)
            {
                Trace.TraceError("输入行数超出了战斗结果表Origin Damage Table的行数");
                return -1;
            }
            if (randomValue < 2) randomValue = 2;
            if (randomValue > 12) randomValue = 12;
            return originDamageTab[oriDamageTabRow, randomValue - 2];
        }

        public int GetDamageRect(string armor, int randomRect)
        {
            if (randomRect < -3) randomRect = -3;
            if (randomRect > 12) randomRect = 12;
            int row = -1;
            switch (armor)
            {
                case "None":
                    row = 0;
                    break;
                case "Light":
                    row = 1;
                    break;
                case "Medium":
                    row = 2;
                    break;
                case "Heavy":
                    row = 3;
                    break;
                case "Composite":
                    row = 4;
                    break;
            }
            if (row < 0)
            {
                Trace.TraceError($"传入装甲类型{armor}有误");
                throw new Exception($"传入装甲类型{armor}有误");
            }
            return damageRectTab[row, randomRect + 3];
        }

        /// <summary>
        /// 直瞄武器对人员/步兵轻武器对车辆战斗结果表
        /// </summary>
        public int GetToInfantryOriDamageTab(int randomValue, int attackLevel)
        {
            if (attackLevel <= 0)   // 出错
            {
                Trace.TraceError("攻击等级Attack Level计算有误，必须大于0！");
                return -1;
            }
            if (attackLevel > 10) attackLevel = 10;
            return toInfantryOriDamageTab[randomValue - 2, attackLevel - 1];  // 数组从0开始计数
        }

        public int GetToInfantryDamageRect(int randomRect)
        {
            if (randomRect < 0) randomRect = 0;
            if (randomRect > 8) randomRect = 8;
            return toInfantryDamageRectTab[randomRect];
        }

        private Weapon GetWeaponFromFile(string file)
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(file);

            XmlNodeList classNodes = xmlDoc.SelectNodes("class");

            if (classNodes.Count > 0)
            {
                XmlNode classNode = classNodes[0];
                string weaponName;
                string bullet;
                try
                {
                    weaponName = classNode["name"].InnerText;
                }
                catch (Exception ex)
                {
                    throw new Exception($"武器文件{file}缺少name属性：" + ex);
                }
                try
                {
                    bullet = classNode["bullet"].InnerText;
                }
                catch (Exception ex)
                {
                    throw new Exception($"武器文件{file}缺少bullet属性：" + ex);
                }

                int rangeVehicle = -1;
                //int[] attackLevelsVehicle = null;
                Dictionary<int, int[]> attackLevelsVehicle = null;
                int rangeInfantry = -1;
                Dictionary<int, int[]> attackLevelsInfantry = null;
                //Dictionary<int, int[]> attackLevelsVehicleOfInfantryLightWeapon = null;

                XmlNodeList fieldNodes = xmlDoc.SelectNodes("/class/field");
                foreach (XmlNode field in fieldNodes)
                {
                    switch (field["identifier"].InnerText)
                    {
                        case "rangeVehicle":
                            rangeVehicle = int.Parse(field["value"].InnerText);
                            break;
                        case "attackLevelsVehicle":
                            attackLevelsVehicle = new Dictionary<int, int[]>();
                            string[] allValue = field["value"].InnerText.Split(';');
                            for (int i = 0; i < allValue.Length; i++)
                            {
                                attackLevelsVehicle[i + 1] = Array.ConvertAll(allValue[i].Split(','), int.Parse);
                            }
                            break;
                        case "rangeInfantry":
                            rangeInfantry = int.Parse(field["value"].InnerText);
                            break;
                        case "attackLevelsInfantry":
                            attackLevelsInfantry = new Dictionary<int, int[]>();
                            string[] allValues = field["value"].InnerText.Split(';');
                            for (int i = 0; i < allValues.Length; i++)
                            {
                                attackLevelsInfantry[i + 1] = Array.ConvertAll(allValues[i].Split(','), int.Parse);
                            }
                            break;
                    }
                }

                //if (weaponName == WeaponType.InfantryLightWeapon)
                //    return new Weapon(weaponName, bullet, rangeVehicle, attackLevelsVehicleOfInfantryLightWeapon, rangeInfantry, attackLevelsInfantry);
                //else
                return new Weapon(weaponName, bullet, rangeVehicle, attackLevelsVehicle, rangeInfantry, attackLevelsInfantry);
            }
            else
            {
                throw new Exception($"武器文件{file}格式有错误，根节点应该是class!");
            }
        }
    }
}
